﻿using System;
using System.Collections.Generic;

namespace CoreRay
{
    /// <summary>
    /// Stores vertices in a buffer with a specific vertex format.
    /// </summary>
    public class VertexBuffer
    {
        private int currentVertexIndex;
        private int currentElementIndex;
        private List<object[]> vertexBuffer;

        /// <summary>
        /// Initializes a new instance of the <see cref="VertexBuffer"/> class.
        /// </summary>
        /// <param name="vertexFormat">Vertex format used by the vertex buffer.</param>
        public VertexBuffer(VertexFormat vertexFormat)
        {
            //Init private members
            this.VertexFormat = vertexFormat;
            this.currentVertexIndex = 0;
            this.currentElementIndex = 0;

            this.vertexBuffer = new List<object[]>();
            this.vertexBuffer.Add(new object[VertexFormat.ElementsCount]);
        }

        /// <summary>
        /// Gets the vertex format.
        /// </summary>
        /// <value>Vertex format of the vertex buffer.</value>
        public VertexFormat VertexFormat
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the count vertices in the buffer.
        /// </summary>
        /// <value>Count of vertices.</value>
        public int Count
        {
            get
            {
                return this.vertexBuffer.Count - 1;
            }
        }

        /// <summary>
        /// Gets a vertex element from the vertex buffer at the specified index with the specified elementName.
        /// </summary>
        /// <typeparam name="T">Type of the vertex element to retrieve.</typeparam>
        /// <param name="vertexIndex">Index of the vertex in the vertex buffer.</param>
        /// <param name="elementName">Name of the element.</param>
        /// <returns>Value of the vertex element.</returns>
        public T GetVertexElement<T>(int vertexIndex, string elementName) where T : struct
        {
            // Check to see if vertexIndex is in range
            if(vertexIndex >= this.vertexBuffer.Count)
            {
                throw new ArgumentOutOfRangeException("vertexIndex");
            }

            // Check to see if elementName exists
            if(!this.VertexFormat.Contains(elementName))
            {
                throw new ArgumentException(string.Format("Element name '{0}' doesn't exist in the vertex buffer.", elementName), "elementName");
            }

            //Return the value
            return (T) this.vertexBuffer[vertexIndex][this.VertexFormat.IndexOf(elementName)];
        }

        /// <summary>
        /// Writes the value in the vertex buffer.
        /// </summary>
        /// <typeparam name="T">Type of the value to write.</typeparam>
        /// <param name="value">Value to write to the vertex buffer.</param>
        public void Write<T>(T value) where T : struct
        {
            // Check the type of the specified value with the current vertex element type
            if(this.VertexFormat[this.currentElementIndex].Type != typeof(T))
            {
                throw new ArgumentException(string.Format("Wrong type for the current vertex element. Expected '{0}'.", this.VertexFormat[this.currentElementIndex].Type.ToString()), "value");
            }

            // Add the element to the vertex dictionary
            this.vertexBuffer[this.currentVertexIndex][this.currentElementIndex] = value;

            // Update indexes
            this.currentElementIndex++;

            if(this.VertexFormat.ElementsCount == this.currentElementIndex)
            {
                this.currentVertexIndex++;
                this.currentElementIndex = 0;

                this.vertexBuffer.Add(new object[VertexFormat.ElementsCount]);
            }
        }
    }
}
